//**********************************************************************
//	Programmer	-	Sujal Bista
//	File Name	-	Main.cpp
//	Description	:	
//					Basic DX example
//					This example is based on the DX11 Tutorial 7
//					released with DirectX SDK (June 2010)
//
//**********************************************************************

#include "global.h"
#include "resource.h"
#include "utilityFunctions.h"
#include "camera.h"
#include "graphicsUtility.h"

#ifdef _MSC_VER
	#ifdef _DEBUG
		#include <crtdbg.h>
		#include <stdlib.h>
		#define DEBUG_NEW new(_NORMAL_BLOCK ,__FILE__, __LINE__)
		#define new DEBUG_NEW	
	#else
		#define DEBUG_NEW new
	#endif		
#endif
#pragma comment(lib,"LibCommon.lib")
#pragma comment(lib,"LibGraphics.lib")

// Structures
struct vertexShaderInput
{
    float3 Pos;
    float2 Tex;
};

struct bufferStatic
{
    float4x4 viewMatrix;
};

struct bufferDynamicOnResize
{
    float4x4 projMatrix;
};

struct bufferDynamicPerFrame
{
    float4x4 worldMatrix;
    float4 color;
	float  tessellationFactor;
	int             uDummy1;
    int             uDummy2;
	int             uDummy3;
};

//--------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------

namespace global
{
	HINSTANCE hInstance;// current instance
	HWND hWnd;			// handle to main window
	HDC hDC;			// handle to main device context
	HGLRC hRC;			// handle to opengl rendering context
	unsigned int width;	// width of the main window
	unsigned int height;// width of the main window
};					 

graphicsUtility *glu=NULL;
graphics* gl=NULL;
device *gDevice=NULL;
deviceContext *dContext=NULL;
texture2D *rTarget=NULL;
texture2D *dStencil=NULL;
renderTargetView *rTargetView=NULL;
depthStencilView *dStencilView=NULL;
shaderVertex *vShader=NULL;	//vertex
shaderHull *hShader=NULL;	//hull
shaderDomain *dShader=NULL;	//domain
shaderGeometry *gShader=NULL;	//geometry
shaderPixel *pShader=NULL;	//pixel
shaderCompute *cShader=NULL;	//compute
inputLayout *vertexLayout=NULL; //vertex input layout
buffer *vertexBuffer=NULL;
buffer *indexBuffer=NULL;
buffer *constantBuffer=NULL;
buffer *changesOnResizeBuffer=NULL;
buffer *dynamicBuffer=NULL;
shaderResourceView *seaFloorTextureSRV=NULL;
samplerState *seaFloorSampler=NULL;
rasterizerState *solidStateMode=NULL;
rasterizerState *wireFrameStateMode=NULL;

deviceContext *deferredContext=NULL;
commandList* deferredCommandList=NULL;


camera cam;
float4x4 worldMatrix;
float4x4 viewMatrix;
float4x4 projMatrix;
float4 color(0.7f, 0.7f, 0.7f, 1.0f);
float elapsedTime=0;

#ifdef _USE_DIRECTX_
	char shaderFileName[]="Shaders/basicShader.fx";
#endif
#ifdef _USE_OPENGL_
	char shaderFileName[]="Shaders/basicShader.glfx";
#endif

//shader related variables
unsigned int bufferStaticIndex=NULL;
unsigned int bufferDynamicOnResizeIndex=NULL;
unsigned int bufferDynamicPerFrameIndex=NULL;
unsigned int diffuseTextureIndex=NULL;

//--------------------------------------------------------------------------------------
// Forward declarations

// Register class and create window
bool setupWindow( HINSTANCE hInstance, int nCmdShow,char *windowClassName,char *windowTitle,unsigned int width, unsigned int height,bool fullScreen);
//initlizes device
bool initDevice(void);
//tests compute shader
bool initAndRunComputeShader(void);
// Called every time the application receives a message
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
// Clean up the objects we've created
void cleanupDevice(void);

//update the scene
void update(void);
//render the scene
void render(void);

// Entry point to the program.
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
	// Register class and create window
    if(!setupWindow(hInstance,nCmdShow,"BasicClass","Basic",800,600,false))
        return 0;

	//init the device
    if(!initDevice())
    {
        cleanupDevice();
        return 0;
    }

	//tests compute shader
    if(!initAndRunComputeShader())
    {
        cleanupDevice();
        return 0;
    }

    // Main message loop
    MSG msg = {0};
    while( WM_QUIT != msg.message )
    {
        if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
        {
			//update the scene
			update();
			//render the scene
            render();
        }
    }

    cleanupDevice();

	#ifdef _DEBUG
		//check for memory leak
		_CrtDumpMemoryLeaks();	
	#endif

    return ( int )msg.wParam;
}

// Register class and create window
bool setupWindow( HINSTANCE hInstance, int nCmdShow,char *windowClassName,char *windowTitle,unsigned int width, unsigned int height,bool fullScreen)
{
	//converts ansi to unicode
	WCHAR *windowClassNameUnicode=ansiToUnicode(windowClassName);
	WCHAR *windowTitleUnicode=ansiToUnicode(windowTitle);


    // Register class
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof( WNDCLASSEX );
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_BASIC);
    wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
    wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = windowClassNameUnicode;
    wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_BASIC);
	
	//register the class
    if(!RegisterClassEx(&wcex ))
	{
		delete[] windowClassNameUnicode;
		delete[] windowTitleUnicode;
        return false;
	}

    // Create window
	global::hInstance = hInstance;
	global::width = width;
	global::height = height;


	DWORD dwExStyle;
	DWORD dwStyle;
	RECT  windowRect;

	//check if full screen is required
	if(fullScreen)
	{
		//set up the device mode structure
		DEVMODE screenSettings;
		memset(&screenSettings,0,sizeof(screenSettings));

		screenSettings.dmSize       = sizeof(screenSettings);
		screenSettings.dmPelsWidth  = width;    // screen width
		screenSettings.dmPelsHeight = height;   // screen height
		screenSettings.dmBitsPerPel = 32;     // bits per pixel
		screenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		// attempt to switch to the resolution and bit depth we've selected
		if (ChangeDisplaySettings(&screenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
		{
			// if we can't get fullscreen, let them choose to quit or try windowed mode
			if (MessageBox(NULL, L"Cannot switch to Full Screen Mode. Do you want to try Window Mode?",L"Error", MB_YESNO | MB_ICONEXCLAMATION ) == IDYES)
				fullScreen = FALSE;
			else
			{
				delete[] windowClassNameUnicode;
				delete[] windowTitleUnicode;
				return false;
			}
		}
	}

	// set the window style appropriately, depending on whether we're in fullscreen mode
	if (fullScreen)
	{
		dwExStyle = WS_EX_APPWINDOW;
		dwStyle = WS_POPUP;           // simple window with no borders or title bar
		ShowCursor(FALSE);            // hide the cursor for now
	}
	else
	{
		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		dwStyle = WS_OVERLAPPEDWINDOW;
	}

	// set up the window we're rendering to so that the top left corner is at (0,0)
	// and the bottom right corner is (height,width)
	windowRect.left = 0;
	windowRect.top = 0;
	windowRect.right = (LONG) width;
	windowRect.bottom = (LONG) height;

	// change the size of the rect to account for borders, etc. set by the style
	AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);

	//so now create our window
	//init main window handler
	global::hWnd = CreateWindowEx(dwExStyle,	// extended style
						windowClassNameUnicode,		// class name
						windowTitleUnicode,			// app name
						dwStyle |				// window style
						WS_CLIPCHILDREN |		// required for
						WS_CLIPSIBLINGS,		// using OpenGL
						CW_USEDEFAULT, CW_USEDEFAULT,					// x,y coordinate
						windowRect.right-windowRect.left,	// width
						windowRect.bottom-windowRect.top,	// height
						NULL,					// handle to parent
						NULL,					// handle to menu
						hInstance,				// application instance
						NULL);					// no extra params

	//free
	delete[] windowClassNameUnicode;
	delete[] windowTitleUnicode;

    if( !global::hWnd)
        return false;
	global::hDC=GetDC(global::hWnd);
	if(!global::hDC)							// Did We Get A Device Context?
	{
		MessageBox(NULL,L"Can't Create A GL Device Context.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	//show the window
    ShowWindow(global::hWnd, nCmdShow );
	UpdateWindow(global::hWnd);
	SetForegroundWindow(global::hWnd);
	SetFocus(global::hWnd);

    return true;
}

//initlizes device
bool initDevice(void)
{
	glu=new graphicsUtility();
	glu->init(global::hWnd,global::width,global::height);

	//get the locations
	gl=&glu->getGraphics();
	gDevice=&glu->getDevice();
	dContext=&glu->getDeviceContext();	
	rTarget=&glu->getRenderTarget();
	dStencil=&glu->getDepthStencil();
	rTargetView=&glu->getRenderTargetView();
	dStencilView=&glu->getDepthStencilView();
		
   	//vertex input layout
	vertexLayout=new inputLayout; 

	graphics::inputElementDesc inputDesc[]=
	{
		{ graphicsDefinitions::INPUT_DATA_TYPE_POSITION, 0, graphicsDefinitions::DATA_FORMAT_R32G32B32_FLOAT, 0, 0, graphicsDefinitions::INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
        { graphicsDefinitions::INPUT_DATA_TYPE_TEXCOORD, 0, graphicsDefinitions::DATA_FORMAT_R32G32_FLOAT, 0, 12, graphicsDefinitions::INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
	};
	unsigned int inputDescCount=sizeof(inputDesc)/sizeof(graphics::inputElementDesc);
		
	//allocate shaders
	vShader=new shaderVertex;
	hShader=new shaderHull;
	dShader=new shaderDomain;
	gShader=new shaderGeometry;
	pShader=new shaderPixel;
	cShader=new shaderCompute;

	/////////////////////////////////////////

	//vertex shader data
	blob *vShaderData=new blob;
	if(!gl->compileShaderFromFile(shaderFileName, "VS", "vs_5_0",vShaderData))
	{
		delete vShaderData;
		return false;		
	}
	
	//create vertex shader
	if(!gDevice->createVertexShader(vShaderData,vShader))
	{
		delete vShaderData;
		return false;
	}

	//{
	//	//shader reflection
	//	ID3D11ShaderReflection* pReflector = NULL;
	//	HRESULT hr = D3DReflect(
	//		vShaderData->data->GetBufferPointer(),
	//		vShaderData->data->GetBufferSize(),
	//		IID_ID3D11ShaderReflection,
	//		(void**) &pReflector);
	//	 if(!SUCCEEDED( hr ) )
	//	 {
	//		 unsigned int i=0;
	//		 i=i;
	//	 }
	//
	//	D3D11_SHADER_DESC desc;
	//	pReflector->GetDesc( &desc );

	//	for (unsigned int i = 0; i < desc.InputParameters; i++ )
	//	{
	//		D3D11_SIGNATURE_PARAMETER_DESC input_desc;
	//		pReflector->GetInputParameterDesc( i, &input_desc );
	//	}

	//	unsigned int slots=pReflector->GetNumInterfaceSlots();
	//	slots=slots;

	//	for (unsigned int i = 0; i < desc.ConstantBuffers; i++ )
	//	{
	//		D3D11_SHADER_BUFFER_DESC  BufferLayout;
	//		ID3D11ShaderReflectionConstantBuffer* pConstBuffer = pReflector->GetConstantBufferByIndex( i );
	//		pConstBuffer->GetDesc( &BufferLayout);
	//		i=i;
	//	}


	//	D3D11_SHADER_INPUT_BIND_DESC pDesc;
	//	hr=pReflector->GetResourceBindingDescByName("bufferDynamicPerFrame",&pDesc);
	//	if(!SUCCEEDED( hr ) )
	//	{
	//		unsigned int i=0;
	//		i=i;
	//	}
	//	pDesc=pDesc;

	//	hr=pReflector->GetResourceBindingDescByName("bufferDynamicOnResize",&pDesc);
	//	if(!SUCCEEDED( hr ) )
	//	{
	//		unsigned int i=0;
	//		i=i;
	//	}
	//	pDesc=pDesc;

	//	hr=pReflector->GetResourceBindingDescByName("bufferStatic",&pDesc);
	//	if(!SUCCEEDED( hr ) )
	//	{
	//		unsigned int i=0;
	//		i=i;
	//	}
	//	pDesc=pDesc;

	//	//fix this part
	//}

	//hull shader data
	blob *hShaderData=new blob;
	if(!gl->compileShaderFromFile(shaderFileName, "HS", "hs_5_0", hShaderData))
	{
		delete hShaderData;
		return false;
	}

	//create hull shader
	if(!gDevice->createHullShader(hShaderData,hShader))
	{
		delete hShaderData;
		return false;
	}

	delete hShaderData;

	//domain shader data
	blob *dShaderData=new blob;
	if(!gl->compileShaderFromFile(shaderFileName, "DS", "ds_5_0", dShaderData))
	{
		delete dShaderData;
		return false;
	}

	//create domain shader
	if(!gDevice->createDomainShader(dShaderData,dShader))
	{
		delete dShaderData;
		return false;
	}

	delete dShaderData;

	//geometry shader data
	blob *gShaderData=new blob;
	if(!gl->compileShaderFromFile(shaderFileName, "GS", "gs_5_0", gShaderData))
	{
		delete gShaderData;
		return false;
	}

	//create geometry shader
	if(!gDevice->createGeometryShader(gShaderData,gShader))
	{
		delete gShaderData;
		return false;
	}

	delete gShaderData;
		
	//pixel shader data
	blob *pShaderData=new blob;
	if(!gl->compileShaderFromFile(shaderFileName, "PS", "ps_5_0", pShaderData))
	{
		delete pShaderData;
		return false;
	}

	//create pixel shader
	if(!gDevice->createPixelShader(pShaderData,pShader))
	{
		delete pShaderData;
		return false;
	}

	delete pShaderData;


	//compute shader data
	blob *cShaderData=new blob;
	if(!gl->compileShaderFromFile(shaderFileName, "CS", "cs_5_0", cShaderData))
	{
		delete cShaderData;
		return false;
	}

	//create compute shader
	if(!gDevice->createComputeShader(cShaderData,cShader))
	{
		delete cShaderData;
		return false;
	}

	delete cShaderData;

	/////////////////////////////////////////
	//link the shaders and get the data indices
	//this step is not used in DX at all
	dContext->VSSetShader(vShader, NULL, 0 );
	dContext->HSSetShader(hShader, NULL, 0 );
	dContext->DSSetShader(dShader, NULL, 0 );
	dContext->GSSetShader(gShader, NULL, 0 );
	dContext->PSSetShader(pShader, NULL, 0 );

	//link buffers to the buffers in the shaders
	dContext->shaderBegin();
	bufferStaticIndex = dContext->getUniformBlockIndex("BufferStatic");
	bufferDynamicOnResizeIndex = dContext->getUniformBlockIndex("BufferDynamicOnResize");
	bufferDynamicPerFrameIndex = dContext->getUniformBlockIndex("BufferDynamicPerFrame");
	diffuseTextureIndex = dContext->getUniformLocation("Diffuse");
	dContext->shaderEnd();	
	/////////////////////////////////////////
		
	//creates input Layout
	gDevice->createInputLayout(inputDesc,inputDescCount,vShaderData,vertexLayout);
	delete vShaderData;
	   
	//// Create vertex buffer
 //   vertexShaderInput vertices[] =
 //   {
 //       { float3( -1.0f, 1.0f, -1.0f ), float2( 0.0f, 0.0f ) },
 //       { float3( 1.0f, 1.0f, -1.0f ), float2( 1.0f, 0.0f ) },
 //       { float3( 1.0f, 1.0f, 1.0f ), float2( 1.0f, 1.0f ) },
 //       { float3( -1.0f, 1.0f, 1.0f ), float2( 0.0f, 1.0f ) },

 //       { float3( -1.0f, -1.0f, -1.0f ), float2( 0.0f, 0.0f ) },
 //       { float3( 1.0f, -1.0f, -1.0f ), float2( 1.0f, 0.0f ) },
 //       { float3( 1.0f, -1.0f, 1.0f ), float2( 1.0f, 1.0f ) },
 //       { float3( -1.0f, -1.0f, 1.0f ), float2( 0.0f, 1.0f ) },

 //       { float3( -1.0f, -1.0f, 1.0f ), float2( 0.0f, 0.0f ) },
 //       { float3( -1.0f, -1.0f, -1.0f ), float2( 1.0f, 0.0f ) },
 //       { float3( -1.0f, 1.0f, -1.0f ), float2( 1.0f, 1.0f ) },
 //       { float3( -1.0f, 1.0f, 1.0f ), float2( 0.0f, 1.0f ) },

 //       { float3( 1.0f, -1.0f, 1.0f ), float2( 0.0f, 0.0f ) },
 //       { float3( 1.0f, -1.0f, -1.0f ), float2( 1.0f, 0.0f ) },
 //       { float3( 1.0f, 1.0f, -1.0f ), float2( 1.0f, 1.0f ) },
 //       { float3( 1.0f, 1.0f, 1.0f ), float2( 0.0f, 1.0f ) },

 //       { float3( -1.0f, -1.0f, -1.0f ), float2( 0.0f, 0.0f ) },
 //       { float3( 1.0f, -1.0f, -1.0f ), float2( 1.0f, 0.0f ) },
 //       { float3( 1.0f, 1.0f, -1.0f ), float2( 1.0f, 1.0f ) },
 //       { float3( -1.0f, 1.0f, -1.0f ), float2( 0.0f, 1.0f ) },

 //       { float3( -1.0f, -1.0f, 1.0f ), float2( 0.0f, 0.0f ) },
 //       { float3( 1.0f, -1.0f, 1.0f ), float2( 1.0f, 0.0f ) },
 //       { float3( 1.0f, 1.0f, 1.0f ), float2( 1.0f, 1.0f ) },
 //       { float3( -1.0f, 1.0f, 1.0f ), float2( 0.0f, 1.0f ) },
 //   };
	//unsigned int verticesCount=sizeof(vertices)/sizeof(vertexShaderInput);

	//// Create index buffer
 //   unsigned short indices[] =
 //   {
 //       3,1,0,
 //       2,1,3,

 //       6,4,5,
 //       7,4,6,

 //       11,9,8,
 //       10,9,11,

 //       14,12,13,
 //       15,12,14,

 //       19,17,16,
 //       18,17,19,

 //       22,20,21,
 //       23,20,22
 //   };
	//unsigned int indicesCount=sizeof(indices)/sizeof(unsigned short);

	////create new vertex buffer
	//vertexBuffer=new buffer;

	////buffer desc
	//graphics::bufferDesc desc;
	//desc.byteWidth=sizeof(vertexShaderInput)*verticesCount;
	//desc.usage=graphicsDefinitions::USAGE_DEFAULT;
	//desc.bindFlags=graphicsDefinitions::BIND_FLAG_VERTEX_BUFFER;
	//desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_NONE;
	//desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_NONE;
	//desc.structureByteStride=0;

	////create buffer in GPU
	//gDevice->createBuffer(desc,vertices,0,0,vertexBuffer);
	//		
	////create index buffer
	//indexBuffer=new buffer;

	////buffer desc
	//desc.byteWidth=sizeof(unsigned short)*indicesCount;
	//desc.usage=graphicsDefinitions::USAGE_DEFAULT;
	//desc.bindFlags=graphicsDefinitions::BIND_FLAG_INDEX_BUFFER;
	//desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_NONE;
	//desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_NONE;
	//desc.structureByteStride=0;

	////create buffer in GPU
	//gDevice->createBuffer(desc,indices,0,0,indexBuffer);

	///////////////////////////////////////////////////////////////////////////
	{
		bucketList<graphicsUtility::vertexPT> vertexData(7);
		bucketList<unsigned short> indexData(7);

		// Create vertex buffer
		graphicsUtility::vertexPT vertices[] =
		{
			graphicsUtility::vertexPT(float3( -1.0f, 1.0f, -1.0f ), float2( 0.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, 1.0f, -1.0f ), float2( 1.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, 1.0f, 1.0f ), float2( 1.0f, 1.0f )),
			graphicsUtility::vertexPT(float3( -1.0f, 1.0f, 1.0f ), float2( 0.0f, 1.0f )),

			graphicsUtility::vertexPT(float3( -1.0f, -1.0f, -1.0f ), float2( 0.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, -1.0f, -1.0f ), float2( 1.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, -1.0f, 1.0f ), float2( 1.0f, 1.0f )),
			graphicsUtility::vertexPT(float3( -1.0f, -1.0f, 1.0f ), float2( 0.0f, 1.0f )),

			graphicsUtility::vertexPT(float3( -1.0f, -1.0f, 1.0f ), float2( 0.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( -1.0f, -1.0f, -1.0f ), float2( 1.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( -1.0f, 1.0f, -1.0f ), float2( 1.0f, 1.0f )),
			graphicsUtility::vertexPT(float3( -1.0f, 1.0f, 1.0f ), float2( 0.0f, 1.0f )),

			graphicsUtility::vertexPT(float3( 1.0f, -1.0f, 1.0f ), float2( 0.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, -1.0f, -1.0f ), float2( 1.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, 1.0f, -1.0f ), float2( 1.0f, 1.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, 1.0f, 1.0f ), float2( 0.0f, 1.0f )),

			graphicsUtility::vertexPT(float3( -1.0f, -1.0f, -1.0f ), float2( 0.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, -1.0f, -1.0f ), float2( 1.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, 1.0f, -1.0f ), float2( 1.0f, 1.0f )),
			graphicsUtility::vertexPT(float3( -1.0f, 1.0f, -1.0f ), float2( 0.0f, 1.0f )),

			graphicsUtility::vertexPT(float3( -1.0f, -1.0f, 1.0f ), float2( 0.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, -1.0f, 1.0f ), float2( 1.0f, 0.0f )),
			graphicsUtility::vertexPT(float3( 1.0f, 1.0f, 1.0f ), float2( 1.0f, 1.0f )),
			graphicsUtility::vertexPT(float3( -1.0f, 1.0f, 1.0f ), float2( 0.0f, 1.0f ))
		};
		unsigned int verticesCount=sizeof(vertices)/sizeof(vertexShaderInput);

		// Create index buffer
		unsigned short indices[] =
		{
			3,0,1,
			2,3,1,

			6,5,4,
			7,6,4,

			11,8,9,
			10,11,9,

			14,13,12,
			15,14,12,

			19,16,17,
			18,19,17,

			22,21,20,
			23,22,20
		};
		unsigned int indicesCount=sizeof(indices)/sizeof(unsigned short);
			
		//add the data
		vertexData.add(vertices,verticesCount);
        indexData.add(indices,indicesCount);

		//creates vertex buffers
		vertexBuffer=new buffer;
		indexBuffer=new buffer;

		glu->createVertexBuffers(*dContext, vertexData, indexData, *vertexBuffer, *indexBuffer);
	}

	///////////////////////////////////////////////////////////////////////////
			
	//create constant buffer
	constantBuffer=new buffer;	
	
	//buffer desc
	graphics::bufferDesc desc;
	desc.byteWidth=sizeof(bufferStatic);
	desc.usage=graphicsDefinitions::USAGE_DEFAULT;
	desc.bindFlags=graphicsDefinitions::BIND_FLAG_CONSTANT_BUFFER;
	desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_NONE;
	desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_NONE;
	desc.structureByteStride=0;

	//create buffer in GPU
	gDevice->createBuffer(desc,NULL,0,0,constantBuffer);
	
	changesOnResizeBuffer=new buffer;

	//buffer desc
	desc.byteWidth=sizeof(bufferDynamicOnResize);
	desc.usage=graphicsDefinitions::USAGE_DEFAULT;
	desc.bindFlags=graphicsDefinitions::BIND_FLAG_CONSTANT_BUFFER;
	desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_NONE;
	desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_NONE;
	desc.structureByteStride=0;

	//create buffer in GPU
	gDevice->createBuffer(desc,NULL,0,0,changesOnResizeBuffer);
	
	dynamicBuffer=new buffer;

	//buffer desc
	desc.byteWidth=sizeof(bufferDynamicPerFrame);
	desc.usage=graphicsDefinitions::USAGE_DEFAULT;
	desc.bindFlags=graphicsDefinitions::BIND_FLAG_CONSTANT_BUFFER;
	desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_NONE;
	desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_NONE;
	desc.structureByteStride=0;

	//create buffer in GPU
	gDevice->createBuffer(desc,NULL,0,0,dynamicBuffer);

	//load the texture	
	seaFloorTextureSRV=new shaderResourceView;
	gDevice->createShaderResourceViewFromFile("..\\media\\texture\\logo.bmp",seaFloorTextureSRV);

	////////////////////////////////////////////////
	////create a blank texture
	//texture2D seaFloorTexture;
	//graphics::texture2DDesc seaFloorDesc;
	//seaFloorDesc.width=512;
	//seaFloorDesc.height=512;
	//seaFloorDesc.mipLevels=1;
	//seaFloorDesc.arraySize=1;
	//seaFloorDesc.format=graphicsDefinitions::DATA_FORMAT_R8G8B8A8_UNORM;
	//seaFloorDesc.sampleDesc.count = 1;
 //   seaFloorDesc.sampleDesc.quality = 0;
 //   seaFloorDesc.usage = graphicsDefinitions::USAGE_DEFAULT;
 //   seaFloorDesc.bindFlags =graphicsDefinitions:: BIND_FLAG_SHADER_RESOURCE;
 //   seaFloorDesc.cpuAccessFlags = 0;
 //   seaFloorDesc.miscFlags = 0;
	//gDevice->createTexture2D(seaFloorDesc,0,0,0, &seaFloorTexture);

	////update the resource
	//unsigned int bytesPerPixel=4;
	//unsigned char *textureData=new unsigned char[seaFloorDesc.width*seaFloorDesc.height*bytesPerPixel];
	//for(unsigned int y=0;y<seaFloorDesc.height;y++)
	//{
	//	for(unsigned int x=0;x<seaFloorDesc.width;x++)
	//	{
	//		unsigned int pixelPos=y*seaFloorDesc.width+x;
	//		if(x==y)
	//			textureData[pixelPos*bytesPerPixel]=255;
	//		else
	//			textureData[pixelPos*bytesPerPixel]=0;
	//		textureData[pixelPos*bytesPerPixel+1]=0;
	//		textureData[pixelPos*bytesPerPixel+2]=0;
	//		textureData[pixelPos*bytesPerPixel+3]=255;
	//	}
	//}

	//graphics::box updateArea;
	//updateArea.left=0;
	//updateArea.right=512;
	//updateArea.top=0;
	//updateArea.bottom=512;
	//updateArea.front=0;
	//updateArea.back=1;

	//dContext->updateSubResource(&seaFloorTexture, 0,&updateArea, textureData, seaFloorDesc.width*4, seaFloorDesc.width*seaFloorDesc.height*4);
	//delete[] textureData;

	////shaderResourceView seaFloorSRV;
	////gDevice->createShaderResourceView(&seaFloorTexture,graphicsDefinitions::DATA_FORMAT_R32G32B32A32_FLOAT,graphicsDefinitions::SRV_DIMENSION_TEXTURE2D,0,1,0,0,&seaFloorSRV);
	////
	//
	//delete seaFloorTextureSRV;
	//seaFloorTextureSRV=new shaderResourceView;
	//gDevice->createShaderResourceView(&seaFloorTexture,graphicsDefinitions::DATA_FORMAT_R8G8B8A8_UNORM,graphicsDefinitions::SRV_DIMENSION_TEXTURE2D,0,(unsigned int)-1,0,0,seaFloorTextureSRV);
	//	
	////////////////////////////////////////////////

	// Create the sample state
	seaFloorSampler=new samplerState;
	graphicsDefinitions::TEXTURE_ADDRESS_MODE addMode[3]={
		graphicsDefinitions::TEXTURE_ADDRESS_MODE_WRAP,
		graphicsDefinitions::TEXTURE_ADDRESS_MODE_WRAP,
		graphicsDefinitions::TEXTURE_ADDRESS_MODE_WRAP
	};
	float borderColor[4]={0,0,0,0};
	gDevice->createSamplerState(
			graphicsDefinitions::FILTER_MIN_MAG_MIP_LINEAR,
			addMode,
			0,
			0,
			graphicsDefinitions::COMPARISON_FUNC_NEVER,
			borderColor,
			0,
			10000,
			seaFloorSampler
		);

		
    // Initialize the world matrices
	worldMatrix.identity();

    // Initialize the view matrix
   	float3 position(0.0f, 3.0f, -6.0f);
	float3 focusPosition(0.0f, 1.0f, 0.0);
	float3 up=float3(0.0f, 1.0f, 0.0f);
	
	//setup camera
	cam.setSize(global::width,global::height);
	cam.setVisibleRange(0.01f,100.0f,45.0f);
	cam.setLookToLH(position,focusPosition,up);
	cam.computeViewMatrixLH();
	cam.computeProjectionMatrixLH();
	viewMatrix=cam.getViewMatrix();
	projMatrix=cam.getProjectionMatrix();
	
   //update the resource
	bufferStatic staticData;
	staticData.viewMatrix = viewMatrix;
	dContext->updateSubResource(constantBuffer, 0, NULL, &staticData, 0, 0 );

	//update the resource
    bufferDynamicOnResize perResizeData;
	perResizeData.projMatrix = projMatrix;
	dContext->updateSubResource(changesOnResizeBuffer, 0, NULL, &perResizeData, 0, 0 );

	//create rasterizer mode
	solidStateMode=new rasterizerState;
	gDevice->createRasterizerState(graphicsDefinitions::FILL_MODE_SOLID,graphicsDefinitions::CULL_MODE_BACK,true,0,0.0f,0.0f,true,false,false,false,solidStateMode);
	wireFrameStateMode=new rasterizerState;
	gDevice->createRasterizerState(graphicsDefinitions::FILL_MODE_WIREFRAME,graphicsDefinitions::CULL_MODE_BACK,true,0,0.0f,0.0f,true,false,false,false,wireFrameStateMode);
	
	//set the rasterizer mode
	dContext->RSSetState(solidStateMode);
	//dContext->RSSetState(wireFrameStateMode);

	//create deferred context
	deferredContext=new deviceContext;
	if(!gDevice->createDeferredContext(deferredContext))
		return false;

	//create new command list
	deferredCommandList=new commandList;
	
	return true;
}

//tests compute shader
bool initAndRunComputeShader(void)
{
	const unsigned int size=256;
	unsigned int dataIn[size];

	for(unsigned int i=0;i<size;i++)
		dataIn[i]=i;

	buffer *computeBufferIn=NULL;
	buffer *computeBufferOut=NULL;
	shaderResourceView *computeBufferInSRV=NULL;
	unorderedAccessView *computeBufferOutUAV=NULL;

	computeBufferIn=new buffer;

	//buffer desc
	graphics::bufferDesc desc;
	desc.byteWidth=size*sizeof(unsigned int);
	desc.usage=graphicsDefinitions::USAGE_DEFAULT;
	desc.bindFlags=(graphicsDefinitions::BIND_FLAG)(graphicsDefinitions::BIND_FLAG_UNORDERED_ACCESS | graphicsDefinitions::BIND_FLAG_SHADER_RESOURCE | graphicsDefinitions::BIND_FLAG_INDEX_BUFFER | graphicsDefinitions::BIND_FLAG_VERTEX_BUFFER);
	desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_NONE;
	desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_BUFFER_ALLOW_RAW_VIEWS;
	desc.structureByteStride=0;

	//create buffer in GPU
	gDevice->createBuffer(desc,dataIn,0,0,computeBufferIn);

	computeBufferOut=new buffer;

	//buffer desc
	desc.byteWidth=size*sizeof(unsigned int);
	desc.usage=graphicsDefinitions::USAGE_DEFAULT;
	desc.bindFlags=(graphicsDefinitions::BIND_FLAG)(graphicsDefinitions::BIND_FLAG_UNORDERED_ACCESS | graphicsDefinitions::BIND_FLAG_SHADER_RESOURCE | graphicsDefinitions::BIND_FLAG_INDEX_BUFFER | graphicsDefinitions::BIND_FLAG_VERTEX_BUFFER);
	desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_NONE;
	desc.miscFlags=graphicsDefinitions::RESOURCE_MISC_FLAG_BUFFER_ALLOW_RAW_VIEWS;
	desc.structureByteStride=0;

	//create buffer in GPU
	gDevice->createBuffer(desc,NULL,0,0,computeBufferOut);

	//create srv
	computeBufferInSRV=new shaderResourceView;
	gDevice->createShaderResourceView(computeBufferIn,graphicsDefinitions::DATA_FORMAT_R32_TYPELESS,graphicsDefinitions::SRV_DIMENSION_BUFFEREX,0,size,0x1,0,computeBufferInSRV);
	
	computeBufferOutUAV=new unorderedAccessView;
	gDevice->createUnorderedAccessView(computeBufferOut,graphicsDefinitions::DATA_FORMAT_R32_TYPELESS,graphicsDefinitions::UAV_DIMENSION_BUFFER,0,size,0x1,computeBufferOutUAV);
	
	//run compute shader
	dContext->CSSetShader(cShader,NULL,0);
	dContext->CSSetShaderResources(0,1,computeBufferInSRV,NULL);

	#ifdef _USE_DIRECTX_
		dContext->CSSetUnorderedAccessViews(0,1,computeBufferOutUAV,NULL);
	#endif
	#ifdef _USE_OPENGL_
		dContext->CSSetUnorderedAccessViews(1,1,computeBufferOutUAV,NULL);
	#endif
	
	//Dispatch the compute shader
	dContext->dispatch(size,1,1);

	//remove the compute shader attachments
	dContext->CSSetShader(NULL,NULL,0);
	shaderResourceView pNULL;
	dContext->CSSetShaderResources(0,1,&pNULL,NULL);
	unorderedAccessView uavNULL;
	#ifdef _USE_DIRECTX_
		dContext->CSSetUnorderedAccessViews(0,1,&uavNULL,NULL);
	#endif
	#ifdef _USE_OPENGL_
		dContext->CSSetUnorderedAccessViews(0,1,&uavNULL,NULL);
	#endif

	//read data back
	buffer computeBufferDebug;

	//buffer desc
	desc.byteWidth=size*sizeof(unsigned int);
	desc.usage=graphicsDefinitions::USAGE_STAGING;
	desc.bindFlags=(graphicsDefinitions::BIND_FLAG)0;
	desc.cpuAccessFlags=graphicsDefinitions::CPU_ACCESS_FLAG_READ;
	desc.miscFlags=(graphicsDefinitions::RESOURCE_MISC_FLAG)0;
	desc.structureByteStride=0;

	//create buffer in GPU
	gDevice->createBuffer(desc,NULL,0,0,&computeBufferDebug);

	//copy to the debug buffer
	dContext->copyResource(&computeBufferDebug,computeBufferOut);

	//map the buffer
	unsigned int rowPitch;
	unsigned int depthPitch;
	unsigned int *dataOut=(unsigned int *)dContext->map(&computeBufferDebug,0,graphicsDefinitions::MAP_READ,rowPitch,depthPitch);

	for(unsigned int i=0;i<size;i++)
	{
		if(dataOut[i]!=i)
			outputDebugString("%s(%d): Error in compute shader.\n",__FILE__,__LINE__);
	}
	outputDebugString("Compute shader completed.\n");

	//unmap the buffer
	dContext->unmap(&computeBufferDebug,0);

	delete computeBufferIn;
	delete computeBufferOut;
	delete computeBufferInSRV;
	delete computeBufferOutUAV;

	computeBufferIn=NULL;
	computeBufferOut=NULL;
	computeBufferInSRV=NULL;
	computeBufferOutUAV=NULL;

	return true;
}

// Called every time the application receives a message
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch( message )
    {
        case WM_PAINT:
            hdc = BeginPaint( hWnd, &ps );
            EndPaint( hWnd, &ps );
            break;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            break;

        default:
            return DefWindowProc( hWnd, message, wParam, lParam );
    }

    return 0;
}

//update the scene
void update(void)
{
	// Update our time
	static DWORD dwTimeStart = 0;
	DWORD dwTimeCur = GetTickCount();
	if( dwTimeStart == 0 )
		dwTimeStart = dwTimeCur;
	elapsedTime = ( dwTimeCur - dwTimeStart ) / 1000.0f;

	// Rotate cube around the origin
	worldMatrix.identity();
	worldMatrix.rotateY(elapsedTime);

	// Modify the color
	color.x = ( sinf( elapsedTime * 1.0f ) + 1.0f ) * 0.5f;
	color.y = ( cosf( elapsedTime * 3.0f ) + 1.0f ) * 0.5f;
	color.z = ( sinf( elapsedTime * 5.0f ) + 1.0f ) * 0.5f;
}

//render the scene
void render(void)
{
	////Clear the back buffer
	//float clearColor[4] = { .90f,.90f,.95f,1.0f }; // red, green, blue, alpha
	//dContext->clearRenderTargetView(rTargetView, clearColor);

	////Clear the depth buffer to 1.0 (max depth)
	//dContext->clearDepthStencilView(dStencilView, graphicsDefinitions::CLEAR_FLAG_DEPTH, 1.0f, 0);

	//just a test to show deferred context

	//clear the deferred state
	deferredContext->clearState();

	// Clear the back buffer
	float clearColor[4] = {.90f,.90f,.95f,1.0f }; // red, green, blue, alpha
	deferredContext->clearRenderTargetView(rTargetView, clearColor);

	// Clear the depth buffer to 1.0 (max depth)
	deferredContext->clearDepthStencilView(dStencilView, graphicsDefinitions::CLEAR_FLAG_DEPTH, 1.0f, 0);

	//finish the command list
	if(!deferredContext->finishCommandList(false,deferredCommandList))
		outputDebugString("Finish command list fail");

	//execute the command list
	dContext->executeCommandList(deferredCommandList,true);
 
	//release the command list
	deferredCommandList->clear();
	

	// Update variables that change once per frame
	bufferDynamicPerFrame perFrameData;
	perFrameData.worldMatrix = worldMatrix;
	perFrameData.color = color;
	perFrameData.tessellationFactor=10.0f;
	dContext->updateSubResource(dynamicBuffer, 0, NULL, &perFrameData, 0, 0 );

	// Render the cube
	dContext->VSSetShader(vShader, NULL, 0 );
	dContext->HSSetShader(hShader, NULL, 0 );
	dContext->DSSetShader(dShader, NULL, 0 );
	dContext->GSSetShader(gShader, NULL, 0 );
	dContext->PSSetShader(pShader, NULL, 0 );

	//called after all the shaders are set before drawing (for opengl shader object) compatibility
	dContext->shaderBegin();
		
	/////////////////////////////////////////////////////////

	//vertex
	dContext->VSSetConstantBuffers(2, 1, dynamicBuffer,bufferDynamicPerFrameIndex);

	//hull
	dContext->HSSetConstantBuffers(2, 1, dynamicBuffer,bufferDynamicPerFrameIndex);

	//geometry
	dContext->GSSetConstantBuffers(0, 1, constantBuffer,bufferStaticIndex);
	dContext->GSSetConstantBuffers(1, 1, changesOnResizeBuffer,bufferDynamicOnResizeIndex);
		
	//pixel
	dContext->PSSetConstantBuffers(2, 1, dynamicBuffer,bufferDynamicPerFrameIndex);
	dContext->PSSetShaderResources(0, 1, seaFloorTextureSRV,diffuseTextureIndex);
	dContext->PSSetSamplers(0, 1, seaFloorSampler);

	/////////////////////////////////////////////////////////


    //Set the input layout
	dContext->setInputLayout(vertexLayout);
	//set vertex buffer
	dContext->setVertexBuffers(0,1,vertexBuffer,sizeof(vertexShaderInput),0);
	//set index buffer
	dContext->setIndexBuffer(indexBuffer,graphicsDefinitions::DATA_FORMAT_R16_UINT,0);
    // Set primitive topology
	dContext->setPrimitiveTopology(graphicsDefinitions::PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
	//draw the non indexed primitive
	dContext->drawIndexed(36,0,0);

	//called after all the drawing is done
	dContext->shaderEnd();

	//print some text
	glu->print(*dContext,float2(0,0),"Basic Tessellated Mesh");
	
	//swaps back and front buffer
	gl->swapBuffer();

	//check for errors
	gl->checkError();
}

// Clean up the objects we've created
void cleanupDevice(void)
{
	delete vShader;	//vertex
	delete hShader;	//hull
	delete dShader;	//domain
	delete gShader;	//geometry
	delete pShader;	//pixel
	delete cShader; //compute
	delete vertexLayout; //vertex input layout
	delete vertexBuffer; //vertex buffer
	delete indexBuffer;	//index buffer
	delete constantBuffer;
	
	delete changesOnResizeBuffer;
	delete dynamicBuffer;
	delete seaFloorTextureSRV;
	delete seaFloorSampler;
	delete solidStateMode;
	delete wireFrameStateMode;
	delete deferredCommandList;
	delete deferredContext;
	delete glu;

	vShader=NULL;	//vertex
	hShader=NULL;	//hull
	dShader=NULL;	//domain
	gShader=NULL;	//geometry
	pShader=NULL;	//pixel
	cShader=NULL;	//compute
	vertexLayout=NULL; //vertex input layout
	vertexBuffer=NULL; //vertex buffer
	indexBuffer=NULL; //index buffer
	constantBuffer=NULL;

	changesOnResizeBuffer=NULL;
	dynamicBuffer=NULL;
	seaFloorTextureSRV=NULL;
	seaFloorSampler=NULL;
	solidStateMode=NULL;
	wireFrameStateMode=NULL;
	deferredCommandList=NULL;
	deferredContext=NULL;
	glu=NULL;

	//release device context
	ReleaseDC(global::hWnd,global::hDC);
}
